﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace CommonUtils
{
    /// <summary>
    /// 反射处理
    /// </summary>
    public static class ReflectionUtil
    {
        public static string GetName<T>()
        {
            return GenericUtil.GetName<T>();
        }
        public static string GetFullName<T>()
        {
            return GenericUtil.GetFullName<T>();
        }

        public static string GetAssemblyName(this Type type)
        => type.Assembly.GetNameOnly();

        public static string GetNamespace<T>()
        => typeof(T).Namespace;

        /// <summary>
        /// 深复制（字段属性）
        /// </summary>
        public static void FillMembers<T>(this T target, T source, params string[] ignores) where T : class
        {
            var type = typeof(T);
            var members = type.GetMembers();
            foreach (var member in members)
            {
                //log
                //member.Print();
                //member.MemberType.Print();
                //Console.WriteLine();

                if (ignores.Contains(member.Name))
                    continue;

                switch (member.MemberType)
                {
                    //字段
                    case MemberTypes.Field:
                        var fieldInfo = member.As<FieldInfo>();
                        var fieldValue = fieldInfo.GetValue(source);
                        fieldInfo.SetValue(target, fieldValue);
                        break;

                    //属性
                    case MemberTypes.Property:
                        var propertyInfo = member.As<PropertyInfo>();
                        if (!propertyInfo.CanWrite)
                            continue;
                        var propertyValue = propertyInfo.GetValue(source);
                        propertyInfo.SetValue(target, propertyValue);
                        break;
                }
            }
        }

        /// <summary>
        /// 克隆
        /// </summary>
        public static T Clone<T>(this T source) where T : class
        {
            var target = Activator.CreateInstance<T>();
            target.FillMembers(source);
            return target;
        }

        public static void CopyMembersFrom<T>(this T target, T source, params string[] ignores) where T : class
        => target.FillMembers(source);

        /// <summary>
        /// 根据名字得到类型
        /// 这个要在外层调用，CommonUtils属于被引用层，权限较低，此处示例
        /// </summary>
        public static Type GetTypeByName(string name)
        => Type.GetType(name, true, true);

        /// <summary>
        /// 根据名字得到成员
        /// 这个要在外层调用，CommonUtils属于被引用层，权限较低，此处示例
        /// </summary>
        public static MemberInfo GetMember(string typeName, string memberName)
        {
            var type = GetTypeByName(typeName);
            return type.GetMembers().FirstOrDefault(m => m.Name.ToLower() == memberName.ToLower());
        }

        public static List<T> GetAttributes<T>(bool inherit = true) where T : Attribute
        {
            var list = new List<T>();
            var method = GetFrame(2).GetMethod();
            list.AddRange(method.GetCustomAttributes<T>(inherit));
            if (inherit && method.DeclaringType != null)
                list.AddRange(method.DeclaringType.GetCustomAttributes<T>(inherit));
            return list;
        }

        public static T GetAttribute<T>(bool inherit = true) where T : Attribute
        => GetAttributes<T>(inherit).FirstOrDefault();

        public static List<T> GetAttributes<T>(this MemberInfo member, bool inherit = true) where T : Attribute
        {
            var list = new List<T>();
            list.AddRange(member.GetCustomAttributes<T>(inherit));
            if (inherit && member.DeclaringType != null)
                list.AddRange(member.DeclaringType.GetCustomAttributes<T>(inherit));
            return list;
        }

        //inherit = porperty inherit class

        public static T GetAttribute<T>(this MemberInfo member, bool inherit = true) where T : Attribute
        => member.GetAttributes<T>(inherit).FirstOrDefault();

        /// <summary>
        /// 当前堆栈
        /// </summary>
        public static readonly StackTrace StackTrace = new StackTrace();

        #region  公共(当前)层级
        public static readonly StackFrame CommonFrame = StackTrace.GetFrame(0);

        public static readonly MethodBase CommonMethod = CommonFrame.GetMethod();

        public static readonly Type CommonType = CommonMethod.ReflectedType;

        public static readonly string CommonTypeName = CommonType.FullName;

        public static readonly string CommonClassName = CommonTypeName;

        public static readonly string CommonMethodName = CommonTypeName + "." + CommonMethod.Name;

        public static readonly string CommonNamespace = CommonType.Namespace;

        public static readonly Assembly CommonAssembly = CommonType.Assembly;

        public static readonly string CommonAssemblyName = CommonAssembly.ManifestModule.Name.Remove(".dll");
        #endregion

        #region 调用层级 sender=current StackTrace要实时获取 '=>'调用也会增加层级
        public static StackFrame GetSenderFrame() => new StackTrace().GetFrame(1);

        public static MethodBase GetSenderMethod() => new StackTrace().GetFrame(1).GetMethod();

        public static string GetSenderMethodName() => new StackTrace().GetFrame(1).GetMethod().Name;

        public static Type GetSenderClassType() => new StackTrace().GetFrame(1).GetMethod().ReflectedType;

        public static string GetSenderClassName() => new StackTrace().GetFrame(1).GetMethod().ReflectedType.FullName;

        public static string GetSenderNamespace() => new StackTrace().GetFrame(1).GetMethod().ReflectedType.Namespace;

        public static Assembly GetSenderAssembly() => new StackTrace().GetFrame(1).GetMethod().ReflectedType.Assembly;

        public static string GetSenderAssemblyName() => new StackTrace().GetFrame(1).GetMethod().ReflectedType.Assembly.ManifestModule.Name.Remove(".dll");

        public static StackFrame GetCurrentFrame() => new StackTrace().GetFrame(1);

        public static MethodBase GetCurrentMethod() => new StackTrace().GetFrame(1).GetMethod();

        public static string GetCurrentMethodName() => new StackTrace().GetFrame(1).GetMethod().Name;

        public static Type GetCurrentClassType() => new StackTrace().GetFrame(1).GetMethod().ReflectedType;

        public static string GetCurrentClassName() => new StackTrace().GetFrame(1).GetMethod().ReflectedType.FullName;

        public static string GetCurrentNamespace() => new StackTrace().GetFrame(1).GetMethod().ReflectedType.Namespace;

        public static Assembly GetCurrentAssembly() => new StackTrace().GetFrame(1).GetMethod().ReflectedType.Assembly;

        public static string GetCurrentAssemblyName() => new StackTrace().GetFrame(1).GetMethod().ReflectedType.Assembly.ManifestModule.Name.Remove(".dll");
        #endregion

        #region 动态（基于调用）层级
        public static StackFrame GetFrame(int index) => new StackTrace().GetFrame(index);

        public static MethodBase GetMethod(int index) => new StackTrace().GetFrame(index).GetMethod();

        public static string GetMethodName(int index) => new StackTrace().GetFrame(index).GetMethod().Name;

        public static Type GetClassType(int index) => new StackTrace().GetFrame(index).GetMethod().ReflectedType;

        public static string GetClassName(int index) => new StackTrace().GetFrame(index).GetMethod().ReflectedType.FullName;

        public static string GetNamespace(int index) => new StackTrace().GetFrame(index).GetMethod().ReflectedType.Namespace;

        public static Assembly GetAssembly(int index) => new StackTrace().GetFrame(index).GetMethod().ReflectedType.Assembly;

        public static string GetAssemblyName(int index) => new StackTrace().GetFrame(index).GetMethod().ReflectedType.Assembly.ManifestModule.Name.Remove(".dll");
        #endregion

        public static PropertyInfo[] GetFields(this object item)
        => item.GetType().GetProperties();

        public static PropertyInfo[] GetFields(this object item, Type type)
        => item.GetType().GetProperties().Where(m => m.PropertyType.GUID == type.GUID).ToArray();

        public static PropertyInfo GetField(this object item, string name)
        => item.GetType().GetProperties().FirstOrDefault(m => m.Name == name);

        public static bool HasId(this object item)
        {
            var field = item.GetField("Id");
            if (field == null)
                return false;
            return StringUtil.IsNotEmpty(field.GetValue(item));
        }
    }
}
